﻿using Microsoft.Extensions.Options;

using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace EOH.Sms.Tencent
{
    /// <summary>腾讯短信服务，暂无法测试，没有注册腾讯服务</summary>
    public class TencentService : IService
    {
        #region 属性
        IHttpClientFactory _HttpClientFactory;
        TencentOptions _TencentOptions;
        static string _HttpClientFactoryName = typeof(TencentService).FullName;
        DateTime _TencentTime = new DateTime(1970, 1, 1, 0, 0, 0);
        #endregion

        #region 构造函数
        /// <summary>构造函数</summary>
        public TencentService(IHttpClientFactory httpClientFactory, IOptions<TencentOptions> options)
        {
            _HttpClientFactory = httpClientFactory;
            _TencentOptions = options?.Value;
        }
        #endregion
        public Task<TResponse> Execute<TResponse>(IRequest<TResponse> request) { throw new Exception(); }
        public async Task<TResponse> Execute<TModel, TResponse>(IRequest<TModel, TResponse> request)
        {
            if (_TencentOptions is null || string.IsNullOrWhiteSpace(_TencentOptions.AccessKeyId) || string.IsNullOrWhiteSpace(_TencentOptions.AccessKeySecret))
            { throw new Exception("AlibabaOptions 配置参数有误"); }
            if (request is TencentRequest<TModel, TResponse> tencentquest)
            {
                try
                {
                    var timestamp = (long)(DateTime.UtcNow - _TencentTime).TotalSeconds;
                    var model = JsonSerializer.Serialize(tencentquest.Model);
                    using var client = _HttpClientFactory.CreateClient(_HttpClientFactoryName);
                    using var alibabaRequest = new HttpRequestMessage(tencentquest.Method, string.Empty);
                    alibabaRequest.Headers.Add($"X-TC-Action", tencentquest.Action);
                    alibabaRequest.Headers.Add($"X-TC-{nameof(TencentOptions.Region)}", _TencentOptions.Region);
                    alibabaRequest.Headers.Add($"X-TC-Timestamp", timestamp.ToString());
                    alibabaRequest.Headers.Add($"X-TC-Version", _TencentOptions.Version);
                    alibabaRequest.Headers.Add($"X-TC-{nameof(TencentOptions.Language)}", _TencentOptions.Language);
                    alibabaRequest.Headers.TryAddWithoutValidation($"Authorization", BuildSignaAuthorization(client, tencentquest.Method, model, timestamp));
                    alibabaRequest.Headers.Add("Host", client.BaseAddress.Host);
                    alibabaRequest.Content = new StringContent(model, Encoding.UTF8, "application/json");
                    var response = await client.SendAsync(alibabaRequest);
                    var content = await response.Content.ReadAsStringAsync();
                    return request.ConvertResponse(content);
                }
                catch (Exception ex) { throw ex; }
            }
            else { throw new Exception("request 非腾讯接口参数实体"); }
        }

        #region 辅助函数
        string BuildSignaAuthorization(HttpClient client, HttpMethod method, string model, long timestamp, string canonicalQueryString = null)
        {
            // ************* 步骤 1：拼接规范请求串 *************
            var httpRequestMethod = $"{method}";
            var canonicalUri = "/";
            canonicalQueryString ??= string.Empty;
            var canonicalHeaders = $"content-type:application/json; charset=utf-8\nhost:{client.BaseAddress.Host}\n";
            var signedHeaders = "content-type;host";
            var hashedRequestPayload = SHA256(model);
            var canonicalRequest = $"{httpRequestMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{hashedRequestPayload}";
            // ************* 步骤 2：拼接待签名字符串 *************
            var date = _TencentTime.AddSeconds(timestamp).ToString("yyyy-MM-dd");
            var service = "sms";
            var algorithm = "TC3-HMAC-SHA256";
            var credentialScope = $"{date}/{service}/tc3_request";
            var stringToSign = $"{algorithm}\n{timestamp}\n{credentialScope}\n{SHA256(canonicalRequest)}";
            // ************* 步骤 3：计算签名 *************
            byte[] tc3SecretKey = Encoding.UTF8.GetBytes("TC3" + _TencentOptions.AccessKeyId);
            byte[] secretDate = HMACSHA256(tc3SecretKey, Encoding.UTF8.GetBytes(date));
            byte[] secretService = HMACSHA256(secretDate, Encoding.UTF8.GetBytes("sms"));
            byte[] secretSigning = HMACSHA256(secretService, Encoding.UTF8.GetBytes("tc3_request"));
            byte[] signatureBytes = HMACSHA256(secretSigning, Encoding.UTF8.GetBytes(stringToSign));
            string signature = BitConverter.ToString(signatureBytes).Replace("-", string.Empty).ToLower();
            // ************* 步骤 4：拼接 Authorization *************
            return $"{algorithm} Credential={_TencentOptions.AccessKeyId}/{credentialScope},SignedHeaders={signedHeaders},Signature={signature}";
        }
        byte[] HMACSHA256(byte[] key, byte[] msg)
        {
            using var hmac = new HMACSHA256(key);
            return hmac.ComputeHash(msg);
        }
        string SHA256(string content, Encoding encoding = null)
        {
            encoding ??= Encoding.UTF8;
            using var sha256 = new SHA256Managed();
            sha256.ComputeHash(encoding.GetBytes(content));
            byte[] hashBytes = sha256.Hash;
            sha256.Clear();
            return BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower();
        }
        #endregion
    }
}
